/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 * Description: yuv数据发送
 */

#ifndef IMAGE_SENDER_YUV_SENDER_H
#define IMAGE_SENDER_YUV_SENDER_H

#include <cstdio>
#include <cstdlib>
#include "image_sender_base.h"
#include "util.h"
#include "mdc/cam/camera/cameradecodedmbufserviceinterface_skeleton.h"

using Skeleton = mdc::cam::camera::skeleton::CameraDecodedMbufServiceInterfaceSkeleton;

struct DealMatPara {
    int32_t h;
    int32_t w;
    int32_t frameCount;
    int32_t frameSize;
    int32_t seq;
};

class YuvSender : public ImageSenderBase<Skeleton> {
public:
    YuvSender() = default;
    YuvSender(uint32_t id, cv::Size s, std::string dir) : ImageSenderBase<Skeleton>(id, s, dir)
    {
        sourceYuvPath = dir;
        if (dir.back() != '/') {
            sourceYuvPath += std::string("/") += "yuvSource.yuv";
        } else {
            sourceYuvPath += "yuvSource.yuv";
        }
#ifdef SAVE_SENDER_FILE
        fpYuv = fopen(sourceYuvPath.c_str(), "wb+");
#endif
        sec = std::chrono::high_resolution_clock::now();
    }
    ~YuvSender() = default;
    bool RegisterSender()
    {
        std::cout << "Begin register normal image sender." << std::endl;
        auto result = ImageSenderBase<Skeleton>::RegisterSender();
        std::cout << "Finished to register normal image sender." << std::endl;
        return result;
    }

    int retYuvSender = -1;
    void FillImageAndSend(std::string yuvPath, uint32_t seq, int32_t *flag) override
    {
        std::string postfix = g_comPara.fileType;
        bool isLoop = g_comPara.loop;
        std::vector<std::string> files {};
        if (GetFilesBySort(dir, files, g_comPara.fileType) == 0) {
            retYuvSender = -1;
            return;
        }
        do {
            for (auto &file : files) {
                FILE *yuvFile = fopen(file.c_str(), "rb");
                if (1 == SwitchFlag) {
                    yuvFile = nullptr;
                }
                if (yuvFile == nullptr) {
                    retYuvSender = -1;
                    return;
                }
                int32_t frameSize = imgSize.width * imgSize.height * 3 / 2;
                if (fseek(yuvFile, 0, SEEK_END) != 0) {
                    retYuvSender = -1;
                    return;
                }
                int32_t fileSize = ftell(yuvFile);
                const int ciTwo = 2;
                if (ciTwo == SwitchFlag) {
                    fileSize = -1;
                }
                if (fileSize == -1L) {
                    retYuvSender = -1;
                    return;
                }
                rewind(yuvFile);
                dealMatPara.h = imgSize.height;
                dealMatPara.w = imgSize.width;
                dealMatPara.frameCount = int32_t (fileSize / frameSize);
                dealMatPara.frameSize = frameSize;
                dealMatPara.seq = seq;
                if (! DealMat(yuvFile, file)) {
                    retYuvSender = -1;
                    return;
                }
            }
        } while (isLoop);
#ifdef SAVE_SENDER_FILE
        if (fclose(fpYuv) == -1) {
            retYuvSender = -1;
            return;
        }
#endif
        retYuvSender = 0;
    }

private:
    void HandleFrequency()
    {
        const int32_t sleepTimeIntervalMs = 1000000;
        if (g_comPara.frequency != "") {
            int32_t freq = atoi(g_comPara.frequency.c_str());
            if (freq != 0) {
                // 1000000微秒
                int32_t sleepTime = sleepTimeIntervalMs / freq;
                usleep(sleepTime);
            }
        } else {
            // 30hz 100000微秒
            usleep(sleepTimeIntervalMs / 30);
        }
    }

    bool DealMat(FILE *yuvFile, std::string &file)
    {
        if (dealMatPara.frameSize < 0) {
            return false;
        }
        char *yuvBuff = (char *)malloc(dealMatPara.frameSize * sizeof(char));
        if (1 == SwitchFlag) {
            yuvBuff = nullptr;
        }
        if (yuvBuff == nullptr) {
            return false;
        }
        for (int32_t i = 0; i < dealMatPara.frameCount; i++) {
            cv::Mat yUV420NV12;
            // yuv height beishu - 1.5
            yUV420NV12.create(dealMatPara.h * 1.5, dealMatPara.w, CV_8UC1);
            if (memset_s(yuvBuff, dealMatPara.frameSize, 0, dealMatPara.frameSize) != 0) {
                return false;
            }
            (void)fread(yuvBuff, sizeof(char), dealMatPara.frameSize, yuvFile);
            memcpy_s(yUV420NV12.data, dealMatPara.frameSize, yuvBuff, dealMatPara.frameSize);
            auto imagePub = dataSkeleton->cameraDecodedMbufEvent.Allocate();
            imagePub->CameraHeader.FrameId = frameID;
            imagePub->CameraHeader.Seq = dealMatPara.seq;
            imagePub->Width = dealMatPara.w;
            imagePub->Height = dealMatPara.h;
            imagePub->DataSize = dealMatPara.frameSize;
            imagePub->UdpData.assign(yUV420NV12.data, yUV420NV12.data + imagePub->DataSize);
#ifdef SAVE_SENDER_FILE
            if (fwrite(yUV420NV12.data, 1, dealMatPara.frameSize, fpYuv) != dealMatPara.frameSize) {
                return false;
            }
#endif
            if (g_comPara.time.empty()) {
                timeval now;
                gettimeofday(&now, NULL);
                imagePub->CameraHeader.Stamp.Sec = now.tv_sec;
                imagePub->CameraHeader.Stamp.Nsec = now.tv_usec * 1000U;
            } else {
                time_t timeStamp = ConvertTimeStr2TimeStamp(g_comPara.time);
                auto t1 = std::chrono::high_resolution_clock::now();
                int64_t duration = (t1 - sec).count() / 1000000000.0;
                imagePub->CameraHeader.Stamp.Sec = timeStamp + duration;
            }
            dataSkeleton->cameraDecodedMbufEvent.Send(move(imagePub));
#ifndef X86LOG
            HandleFrequency();
#endif
        }
        free(yuvBuff);
        SwitchFlag = -1;
        return true;
    }

private:
    std::string sourceYuvPath {};
    FILE *fpYuv { nullptr };
    std::chrono::high_resolution_clock::time_point sec {}; // -t used
    DealMatPara dealMatPara;
};

#endif // IMAGE_SENDER_YUV_SENDER_H
